package cn.itcast.nio.c4;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

import static cn.itcast.nio.c1.ByteBufferUtil.debugAll;

@Slf4j(topic = " ")
public class Server {
    private static void split(ByteBuffer source){
        source.flip();
        for(int i=0;i<source.limit();i++){
            if(source.get(i)=='\n'){
                int length=i+1-source.position();
                System.out.println(length);
                ByteBuffer target=ByteBuffer.allocate(length);
                for(int j=0;j<length;j++){
                    target.put(source.get());
                }
                debugAll(target);
            }
        }
        source.compact();
    }
    public static void main(String[] args) throws IOException {
        //ByteBuffer buffer=ByteBuffer.allocate(16);
        //建立select,管理多个channels
        Selector selector=Selector.open();
        //1.创建服务器
        ServerSocketChannel ssc= ServerSocketChannel.open();
        ssc.configureBlocking(false);//设置为非阻塞模式


        //建立selector与channel的联系
        SelectionKey sscKey=ssc.register(selector,0,null);
        //key只关心accept事件
        sscKey.interestOps(SelectionKey.OP_ACCEPT);
        log.debug("sscKey:{}",sscKey);
        //2.绑定监听端口
        ssc.bind(new InetSocketAddress(8080));

        //3.连接集合
        //List<SocketChannel> channels=new ArrayList<>();

        while(true){
           /* //4.accept建立与客户端的连接，SocketChannel用来与客户端之间通信

            SocketChannel sc=ssc.accept();//非阻塞，没有连接建立就返回null
            if(sc!=null){
                log.debug("connected...{}",sc);
                sc.configureBlocking(false);
                channels.add(sc);
            }
            for(SocketChannel channel:channels){
                //5.接受客户端发送的数据
                int read=channel.read(buffer);
                if(read>0){
                    buffer.flip();
                    debugAll(buffer);
                    buffer.clear();
                    log.debug("after read...{}",channel);
                }
            }*/
            selector.select();
            Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
            while(iter.hasNext()){
                SelectionKey key=iter.next();
                //处理keys，要从selectedKeys集合中删除，避免空指针问题
                iter.remove();
                log.debug("key:{}",key);
                //区分事件类型
                if(key.isAcceptable()){
                    ServerSocketChannel channel=(ServerSocketChannel) key.channel();
                    SocketChannel sc=channel.accept();
                    sc.configureBlocking(false);
                    ByteBuffer buffer=ByteBuffer.allocate(16);
                    SelectionKey sckey = sc.register(selector, 0, buffer);
                    sckey.interestOps(SelectionKey.OP_READ);
                    log.debug("sc:{}",sc);
                    log.debug("scKey:{}",sckey);
                } else if (key.isReadable()) {
                    try {
                        SocketChannel channel=(SocketChannel) key.channel();
                        ByteBuffer buffer=(ByteBuffer) key.attachment();
                        int read=channel.read(buffer);
                        if(read==-1){
                            key.cancel();
                        }else{
                            split(buffer);
                            if(buffer.position()==buffer.limit()){
                                ByteBuffer newBuffer=ByteBuffer.allocate(buffer.capacity()*2);
                                buffer.flip();
                                newBuffer.put(buffer);
                                key.attach(newBuffer);
                            }
                        }
                    } catch (IOException e) {
                       e.printStackTrace();
                        //事件建立后要么建立连接，要么取消，不能置之不理
                        //key.cancel();
                    }
                }
            }
        }
    }
}
